
;;; Class CSCE 625 - Artificial Intelligence
;;; Progam 1
;;; Master student: Duc Anh Nguyen
;;; UIN: 923002585
;;; Class 2015 - Computer Science & Engineering department

;;-----------------------------------------------
;; Deriv functions
;; Support: addition, subtraction, unary minus, 
;; multiplication, division, sqrt, log, exp, sin, 
;; cos, tan
;;-----------------------------------------------
(defun deriv(expr var) 
  (if (atom expr)
    (if (equal expr var) 1 0)
    (cond
        ((eq '+ (first expr))      ; ADDITION
            (deriv-plus expr var)) 
        
        ((eq '- (first expr))      
            (if (eq (length expr) 2)            ; Check: Unary Minus or Subtraction
                (deriv-unary-minus expr var)    ; UNARY MINUS
                (deriv-subtr expr var)          ; SUBTRACTION
            )) 
        
        ((eq '* (first expr))      ; MULTIPLICATION
            (deriv-mult expr var))
        
        ((eq '/ (first expr))      ; DIVISION
            (deriv-div expr var)) 
        
        ((eq 'sqrt (first expr))      ; SQRT
            (deriv-sqrt expr var))
        
        ((eq 'log (first expr))      ; LOG
            (deriv-log expr var)) 
        
        ((eq 'exp (first expr))      ; EXP
            (deriv-exp expr var))

        ((and (eq 'expt (first expr)) (numberp (third expr)))      ; EXPT
            (deriv-expt expr var))
        
        ((eq 'sin (first expr))      ; SIN
            (deriv-sin expr var)) 
        
        ((eq 'cos (first expr))      ; COS
            (deriv-cos expr var))
        
        ((eq 'tan (first expr))      ; TAN
            (deriv-tan expr var)) 

        (t                         ; Invalid
            (error "Invalid Expression!"))
    ) 
  )
)

;;---------------------------------------------
;; Manipulating functions
;;---------------------------------------------
;; deriv-plus function
;;----------------------
(defun deriv-plus (expr var)
    (list '+
          (deriv (second expr) var)
          (deriv (third expr) var)
    )
)

;;----------------------
;; deriv-subtr function
;;----------------------
(defun deriv-subtr (expr var)
    (list '-
          (deriv (second expr) var)
          (deriv (third expr) var)
    )
)

;;----------------------
;; deriv-uanry-minus function
;;----------------------
(defun deriv-unary-minus (expr var)
    (- (deriv (second expr) var))
)

;;----------------------
;; deriv-mult function
;;----------------------
(defun deriv-mult (expr var)
    (list '+
          (list '* (deriv (second expr) var) (third expr))
          (list '* (deriv (third expr) var) (second expr))
    )
)

;;----------------------
;; deriv-div function
;;----------------------
(defun deriv-div (expr var)
    (list '/
          (list '-
                (list '* (deriv (second expr) var) (third expr))
                (list '* (deriv (third expr) var) (second expr))
          )
          (* (third expr) (third expr))
    )
)

;;----------------------
;; deriv-sqrt function
;;----------------------
(defun deriv-sqrt (expr var)
    (list '/
          (deriv (second expr) var)
          (list '* 2 (list 'sqrt (second expr)))
    )
)

;;----------------------
;; deriv-log function
;;----------------------
(defun deriv-log (expr var)
    (list '/
          (deriv (second expr) var)
          (second expr)
    )
)

;;----------------------
;; deriv-exp function
;;----------------------
(defun deriv-exp (expr var)
    (list '*
          (list 'exp (second expr))
          (deriv (second expr) var)
    )
)

;;----------------------
;; deriv-expt function
;;----------------------
(defun deriv-expt (expr var)
    (list '*
          (list '* (third expr) (list 'expt (second expr) (- (third expr) 1)))
          (deriv (second expr) var)
    )
)

;;----------------------
;; deriv-sin function
;;----------------------
(defun deriv-sin (expr var)
    (list '*
          (list 'cos (second expr))
          (deriv (second expr) var)
    )
)

;;----------------------
;; deriv-cos function
;;----------------------
(defun deriv-cos (expr var)
    (list '*
          (list '- (list 'sin (second expr)))
          (deriv (second expr) var)
    )
)

;;----------------------
;; deriv-tan function
;;----------------------
(defun deriv-tan (expr var)
    (list '*
          (list '+ '1 (list '* (list 'tan (second expr)) (list 'tan (second expr)) ))
          (deriv (second expr) var)
    )
)


;;---------------------------------------------
;; Simplifing functions for operators
;;---------------------------------------------
;; Function comment
(defun s-plus(x y)
    (if (numberp x)
        (if (numberp y)
            (+ x y)
            (if (zerop x)
                y
                (list '+ x y) 
            )
        )
        (if (and (numberp y) (zerop y))
            x
            (list '+ x y) 
        )
    )
)

;; Function comment
(defun s-subtr(x y)
    (if (numberp x)
        (if (numberp y)
            (- x y)
            (if (zerop x)
                (- y)
                (list '- x y) 
            )
        )
        (if (and (numberp y) (zerop y))
            x
            (list '- x y) 
        )
    )
    (if(equal x y) 0)
)

;; Function comment
(defun s-unary-minus(x)
    (if (numberp x)
        (if (zerop x)
            0
            (- x))
        (list '- x)
    )
    
)

;; Function comment
(defun s-mult(x y)
    (if (numberp x)
        (if (numberp y)
            (* x y)
            (if (zerop x)
                0
                (if (equal 1 x) 
                    y
                    (list '* x y)) 
            )
        )
        (if (numberp y)
            (if (equal 0 y)
                0
                (if (equal 1 y)
                    x
                    (list '* x y))                
            )
            (list '* x y)
        )        
    )
)

;; Function comment
(defun s-div(x y)
      (if (equal x y) 
          1
      (if (numberp x)
          (if (numberp y)
              (/ x y)
              (if (zerop x)
                 0
                 (list '/ x y))
          )
          (if (numberp y)
              (if (zerop y)
                 (error "Invalid devision by zero!")
                 (if (equal 1 y)
                     x
                     (list '/ x y)))
              (list '/ x y)
          )
      )
      )
)

;; Function comment
(defun s-sqrt(x)
    (if (and (numberp x) (< x 0))
        (error "Invalid SQRT with nagative number!")
        (if (numberp x)
            (sqrt x)
            (list 'sqrt 'x)))
)

;; Function comment
(defun s-log(x)
    (if (and (numberp x) (<= x 0))
        (error "Invalid LOG with non-positive number!")
        (if (numberp x)
            (log x)
            (list 'log 'x)))
)

;; Function comment
(defun s-exp(x)
    (if (numberp x)
        (if (equal 0 x)
            1
            (exp x))
        (list 'exp 'x))
)

;; Function comment
(defun s-sin(x)
    (if (and (numberp x) (equal 0 x))
        0
        (list 'sin x))
)

;; Function comment
(defun s-cos(x)
    (if (and (numberp x) (equal 0 x))
        1
        (list 'cos x))
)

;; Function comment
(defun s-tan(x)
    (if (and (numberp x) (equal 0 x))
        0
        (list 'tan x))
)

;;---------------------------------------------
;; Deriv-eval() functions
;;---------------------------------------------
(defun deriv-eval(expr var val)
   (calc-val (deriv expr var) val)
)

; Function comment
(defun calc-val(expr val)
  (if (numberp expr) ; return expr if expr is number
    (print expr)
    (progn
        ; calculate value of child expressions
        (cond
             ((eq 'x expr)
                  (print val))
             ((eq '+ (first expr))
                  (+ (calc-val (second expr) val) (calc-val (third expr) val)))
             ((eq '- (first expr))
                  (if (eq (length expr) 2)      ; Check: Unary Minus or Subtraction
                      (- (calc-val (second expr) val))
                      (- (calc-val (second expr) val) (calc-val (third expr) val))))
             ((eq '* (first expr))
                  (* (calc-val (second expr) val) (calc-val (third expr) val)))
             ((eq '/ (first expr))
                  (/ (calc-val (second expr) val) (calc-val (third expr) val)))
        )            
    )
  )
)

;;---------------------------------------------
;; Recursive Simplify functions
;;---------------------------------------------
(defun simplify(expr)
  (if (or (eq 'x expr) (numberp expr)) ; return expr if expr = x || expr is number || expr == simplify (expr)
    (progn 
        (print expr))
    (progn
        ; simplify child expressions
        (cond
             ((eq '+ (first expr))
                  (apply-simplify-rules (list '+ (simplify (second expr)) (simplify (third expr))))
             )
             ((eq '- (first expr))
                  (apply-simplify-rules (list '- (simplify (second expr)) (simplify (third expr))))
             )
             ((eq '* (first expr))
                  (apply-simplify-rules (list '* (simplify (second expr)) (simplify (third expr))))
             )
             ((eq '/ (first expr))
                  (apply-simplify-rules (list '/ (simplify (second expr)) (simplify (third expr))))
             )
        )            
    )
  )
)

;Function comment
(defun apply-simplify-rules(expr)
        (cond
             ((and (eq '+ (first expr)) (numberp (second expr)) (numberp (third expr)))  ; number * number
                  (+ (second expr) (third expr))
             )
             ((and (eq '* (first expr)) (numberp (second expr)) (numberp (third expr)))  ; number + number 
                  (* (second expr) (third expr))
             )
             ((and (eq '+ (first expr)) (numberp (second expr)) (equal 0 (second expr)))  ; 0 + expr
                  (third expr)
             )
             ((and (eq '+ (first expr)) (numberp (third expr)) (equal 0 (third expr)))  ; expr + 0
                  (second expr)
             )
             ((and (eq '* (first expr)) (numberp (second expr)) (equal 1 (second expr)))  ; 1 * expr
                  (third expr)
             )
             ((and (eq '* (first expr)) (numberp (third expr)) (equal 1 (third expr)))  ; expr * 1
                  (second expr)
             )
             ((and (eq '* (first expr)) (numberp (second expr)) (equal 0 (second expr)))  ; 0 * expr
                  0
             )
             ((and (eq '* (first expr)) (numberp (third expr)) (equal 0 (third expr)))  ; expr * 0
                  0
             )
             ((and (eq '+ (first expr)) (eq (second expr) (third expr)))  ; expr1 + expr2 (expr1 = expr2)
                  (list '* 2 (second expr))
             )
             ((and (eq '- (first expr)) (eq (second expr) (third expr)))  ; expr1 - expr2 (expr1 = expr2)
                  0
             )             
             ;((and (eq '- (first expr)) (eq '- (first (first expr))))
             ;     (expr) ;;;---------XXXX check this ---------the left of first expr
             ;)
             
             (t                         ; cases cannot simplified
                  (print expr))
        )
)
